home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 2
/
AACD 2.iso
/
AACD
/
Programming
/
fpc
/
compiler
/
pdecl.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1998-09-24
|
67KB
|
1,778 lines
{
$Id: pdecl.pas,v 1.1.1.1.2.2 1998/04/27 23:07:02 peter Exp $
Copyright (c) 1993-98 by Florian Klaempfl
Does declaration parsing for Free Pascal
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit pdecl;
interface
uses
globals,symtable;
var
{ pointer to the last read type symbol, (for "forward" }
{ types) }
lasttypesym : ptypesym;
{ hack, which allows to use the current parsed }
{ object type as function argument type }
testcurobject : byte;
curobjectname : stringid;
{ reads a string type with optional length }
{ and returns a pointer to the string }
{ definition }
function stringtype : pdef;
{ reads a string, file type or a type id and returns a name and }
{ pdef }
function single_type(var s : string) : pdef;
{ reads the declaration blocks }
procedure read_declarations(islibrary : boolean);
{ reads declarations in the interface part of a unit }
procedure read_interface_declarations;
implementation
uses
cobjects,scanner,aasm,tree,pass_1,
types,hcodegen,verbose,systems
{$ifdef GDB}
,gdb
{$endif GDB}
{ parser specific stuff }
,pbase,ptconst,pexpr,psub,pexports
{ processor specific stuff }
{$ifdef i386}
,i386
{$endif}
{$ifdef m68k}
,m68k
{$endif}
;
function read_type(const name : stringid) : pdef;forward;
procedure read_var_decs(is_record : boolean;do_absolute : boolean);forward;
procedure const_dec;
var
name : stringid;
p : ptree;
def : pdef;
ps : pconstset;
pd : pdouble;
begin
consume(_CONST);
repeat
name:=pattern;
consume(ID);
case token of
EQUAL:
begin
consume(EQUAL);
p:=expr;
do_firstpass(p);
case p^.treetype of
ordconstn:
begin
if is_constintnode(p) then
symtablestack^.insert(new(pconstsym,init(name,constint,p^.value,nil)))
else if is_constcharnode(p) then
symtablestack^.insert(new(pconstsym,init(name,constchar,p^.value,nil)))
else if is_constboolnode(p) then
symtablestack^.insert(new(pconstsym,init(name,constbool,p^.value,nil)))
else if p^.resulttype^.deftype=enumdef then
symtablestack^.insert(new(pconstsym,init(name,constord,p^.value,p^.resulttype)))
else internalerror(111);
end;
stringconstn:
{values is disposed with p so I need a copy !}
symtablestack^.insert(new(pconstsym,init(name,conststring,longint(stringdup(p^.values^)),nil)));
realconstn : begin
new(pd);
pd^:=p^.valued;
symtablestack^.insert(new(pconstsym,init(name,constreal,longint(pd),nil)));
end;
setconstrn : begin
new(ps);
ps^:=p^.constset^;
symtablestack^.insert(new(pconstsym,init(name,
constseta,longint(ps),p^.resulttype)));
end;
else Message(cg_e_illegal_expression);
end;
consume(SEMICOLON);
end;
COLON:
begin
{ this was missed, so const s : ^string = nil gives an
error (FK)
}
parse_types:=true;
consume(COLON);
def:=read_type('');
symtablestack^.insert(new(ptypedconstsym,init(name,def)));
parse_types:=false;
consume(EQUAL);
readtypedconst(def);
consume(SEMICOLON);
end;
else consume(EQUAL);
end;
until token<>ID;
end;
procedure label_dec;
var
hl : plabel;
begin
consume(_LABEL);
if not(cs_support_goto in aktswitches) then
Message(sym_e_goto_and_label_not_supported);
repeat
if not(token in [ID,INTCONST]) then
consume(ID)
else
begin
getlabel(hl);
symtablestack^.insert(new(plabelsym,init(pattern,hl)));
consume(token);
end;
if token<>SEMICOLON then consume(COMMA);
until not(token in [ID,INTCONST]);
consume(SEMICOLON);
end;
{ reads a string type with optional length }
{ and returns a pointer to the string }
{ definition }
function stringtype : pdef;
var
p : ptree;
d : pdef;
begin
consume(_STRING);
if token=LECKKLAMMER then
begin
consume(LECKKLAMMER);
p:=expr;
do_firstpass(p);
if not is_constintnode(p) then
Message(cg_e_illegal_expression);
{$ifndef UseLongString}
if (p^.value<1) or (p^.value>255) then
begin
Message(parser_e_string_too_long);
p^.value:=255;
end;
consume(RECKKLAMMER);
if p^.value<>255 then
d:=new(pstringdef,init(p^.value))
{$ifndef GDB}
else d:=new(pstringdef,init(255));
{$else * GDB *}
else d:=globaldef('SYSTEM.STRING');
{$endif * GDB *}
{$else UseLongString}
if p^.value>255 then
d:=new(pstringdef,longinit(p^.value)
else if p^.value<>255 then
d:=new(pstringdef,init(p^.value))
{$ifndef GDB}
else d:=new(pstringdef,init(255));
{$else * GDB *}
else d:=globaldef('SYSTEM.STRING');
{$endif * GDB *}
{$endif UseLongString}
disposetree(p);
end
{$ifndef GDB}
else d:=new(pstringdef,init(255));
{$else * GDB *}
else d:=globaldef('SYSTEM.STRING');
{$endif * GDB *}
stringtype:=d;
end;
{ reads a type definition and returns a pointer }
{ to a appropriating pdef, s gets the name of }
{ the type to allow name mangling }
function id_type(var s : string) : pdef;
begin
s:=pattern;
consume(ID);
if (testcurobject=2) and (curobjectname=pattern) then
begin
id_type:=aktobjectdef;
exit;
end;
getsym(s,true);
if assigned(srsym) then
begin
if srsym^.typ=unitsym then
begin
consume(POINT);
getsymonlyin(punitsym(srsym)^.unitsymtable,pattern);
s:=pattern;
consume(ID);
end;
if srsym^.typ<>typesym then
begin
Message(sym_e_type_id_expected);
lasttypesym:=ptypesym(srsym);
id_type:=generrordef;
exit;
end;
end;
lasttypesym:=ptypesym(srsym);
id_type:=ptypesym(srsym)^.definition;
end;
{ reads a string, file type or a type id and returns a name and }
{ pdef }
function single_type(var s : string) : pdef;
var
hs : string;
begin
case token of
_STRING:
begin
single_type:=stringtype;
s:='STRING';
lasttypesym:=nil;
end;
_FILE:
begin
consume(_FILE);
if token=_OF then
begin
consume(_OF);
single_type:=new(pfiledef,init(ft_typed,single_type(hs)));
s:='FILE$OF$'+hs;
end
else
begin
{ single_type:=new(pfiledef,init(ft_untyped,nil));}
single_type:=cfiledef;
s:='FILE';
end;
lasttypesym:=nil;
end;
else single_type:=id_type(s);
end;
end;
{ this function parses an object or class declaration }
function object_dec(const n : stringid;fd : pobjectdef) : pdef;
var
actmembertype : symprop;
there_is_a_destructor : boolean;
is_a_class : boolean;
childof : pobjectdef;
aktclass : pobjectdef;
procedure constructor_head;
begin
consume(_CONSTRUCTOR);
{ must be at same level as in implementation }
_proc_head(poconstructor);
if (cs_checkconsname in aktswitches) and (aktprocsym^.name<>'INIT') then
Message(parser_e_constructorname_must_be_init);
consume(SEMICOLON);
begin
if (aktclass^.options and oois_class)<>0 then
begin
{ CLASS constructors return the created instance }
aktprocsym^.definition^.retdef:=aktclass;
end
else
begin
{ OBJECT constructors return a boolean }
{$IfDef GDB}
{GDB doesn't like unnamed types !}
aktprocsym^.definition^.retdef:=
globaldef('boolean');
{$Else * GDB *}
aktprocsym^.definition^.retdef:=
new(porddef,init(bool8bit,0,1));
{$Endif * GDB *}
end;
end;
end;
procedure property_dec;
var
sym : psym;
propertyparas : pdefcoll;
{ returns the matching procedure to access a property }
function get_procdef : pprocdef;
var
p : pprocdef;
begin
p:=pprocsym(sym)^.definition;
get_procdef:=nil;
while assigned(p) do
begin
if equal_paras(p^.para1,propertyparas) then
break;
p:=p^.nextoverloaded;
end;
get_procdef:=p;
end;
var
hp2,datacoll : pdefcoll;
p,p2 : ppropertysym;
overriden : psym;
hs : string;
code : word;
varspez : tvarspez;
sc : pstringcontainer;
hp : pdef;
s : string;
begin
{ check for a class }
if (aktclass^.options and oois_class=0) then
Message(parser_e_syntax_error);
consume(_PROPERTY);
if token=ID then
begin
p:=new(ppropertysym,init(pattern));
consume(ID);
propertyparas:=nil;
datacoll:=nil;
{ property parameters ? }
if token=LECKKLAMMER then
begin
{ create a list of the parameters in propertyparas }
consume(LECKKLAMMER);
inc(testcurobject);
repeat
if token=_VAR then
begin
consume(_VAR);
varspez:=vs_var;
end
else if token=_CONST then
begin
consume(_CONST);
varspez:=vs_const;
end
else varspez:=vs_value;
sc:=idlist;
if token=COLON then
begin
consume(COLON);
if token=_ARRAY then
begin
if (varspez<>vs_const) and
(varspez<>vs_var) then
begin
varspez:=vs_const;
Message(parser_e_illegal_open_parameter);
end;
consume(_ARRAY);
consume(_OF);
{ define range and type of range }
hp:=new(parraydef,init(0,-1,s32bitdef));
{ define field type }
parraydef(hp)^.definition:=single_type(s);
end
else
hp:=single_type(s);
end
else
hp:=new(pformaldef,init);
s:=sc^.get;
while s<>'' do
begin
new(hp2);
hp2^.paratyp:=varspez;
hp2^.data:=hp;
hp2^.next:=propertyparas;
propertyparas:=hp2;
s:=sc^.get;
end;
dispose(sc,done);
if token=SEMICOLON then consume(SEMICOLON)
else break;
until false;
dec(testcurobject);
consume(RECKKLAMMER);
end;
{ overriden property ? }
{ force property interface, if there is a property parameter }
if (token=COLON) or assigned(propertyparas) then
begin
consume(COLON);
p^.proptype:=single_type(hs);
if (token=ID) and (pattern='INDEX') then
begin
consume(ID);
p^.options:=p^.options or ppo_indexed;
if token=INTCONST then
val(pattern,p^.index,code);
consume(INTCONST);
{ concat a longint to the para template }
new(hp2);
hp2^.paratyp:=vs_value;
hp2^.data:=s32bitdef;
hp2^.next:=propertyparas;
propertyparas:=hp2;
end;
end
else
begin
{ do an property override }
overriden:=search_class_member(aktclass,pattern);
if assigned(overriden) and (overriden^.typ=propertysym) then
begin
{ take the whole info: }
p^.options:=ppropertysym(overriden)^.options;
p^.index:=ppropertysym(overriden)^.index;
p^.writeaccesssym:=ppropertysym(overriden)^.writeaccesssym;
p^.readaccesssym:=ppropertysym(overriden)^.readaccesssym;
end
else
begin
p^.proptype:=generrordef;
message(parser_e_no_property_found_to_override);
end;
end;
if (token=ID) and (pattern='READ') then
begin
consume(ID);
sym:=search_class_member(aktclass,pattern);
if not(assigned(sym)) then
Message1(sym_e_unknown_id,pattern)
else
begin
{ !!!! check sym }
{ varsym aren't allowed for an indexed property
or an property with parameters }
if ((sym^.typ=varsym) and
(((p^.options and ppo_indexed)<>0) or
assigned(propertyparas))) or
not(sym^.typ in [varsym,procsym]) then
Message(parser_e_ill_property_access_sym);
{ search the matching definition }
if sym^.typ=procsym then
begin
{ !!!!!! }
end;
p^.readaccesssym:=sym;
end;
consume(ID);
end;
if (token=ID) and (pattern='WRITE') then
begin
consume(ID);
sym:=search_class_member(aktclass,pattern);
if not(assigned(sym)) then
Message1(sym_e_unknown_id,pattern)
else
begin
{ !!!! check sym }
if ((sym^.typ=varsym) and
(((p^.options and ppo_indexed)<>0)
{ or property paras })) or
not(sym^.typ in [varsym,procsym]) then
Message(parser_e_ill_property_access_sym);
{ search the matching definition }
if sym^.typ=procsym then
begin
{ !!!!!! }
end;
p^.writeaccesssym:=sym;
end;
consume(ID);
end;
if (token=ID) and (pattern='STORED') then
begin
consume(ID);
{ !!!!!!!! }
end;
if (token=ID) and (pattern='DEFAULT') then
begin
consume(ID);
if token=SEMICOLON then
begin
p2:=search_default_property(aktclass);
if assigned(p2) then
message1(parser_e_only_one_default_property,
pobjectdef(p2^.owner^.defowner)^.name^)
else
begin
p^.options:=p^.options and ppo_defaultproperty;
if not(assigned(propertyparas)) then
message(parser_e_property_need_paras);
end;
end
else
begin
{ !!!!!!! storage }
end;
consume(SEMICOLON);
end
else if (token=ID) and (pattern='NODEFAULT') then
begin
consume(ID);
{ !!!!!!!! }
end;
symtablestack^.insert(p);
{ clean up }
if assigned(datacoll) then
dispose(datacoll);
end
else
consume(ID);
consume(SEMICOLON);
end;
procedure destructor_head;
begin
consume(_DESTRUCTOR);
_proc_head(podestructor);
if (cs_checkconsname in aktswitches) and (aktprocsym^.name<>'DONE') then
Message(parser_e_destructorname_must_be_done);
consume(SEMICOLON);
if assigned(aktprocsym^.definition^.para1) then
Message(parser_e_no_paras_for_destructor);
{ no return value }
aktprocsym^.definition^.retdef:=voiddef;
end;
procedure object_komponenten;
var
oldparse_only : boolean;
begin
repeat
case token of
ID:
begin
if (pattern='PUBLIC') or
(pattern='PUBLISHED') or
(pattern='PROTECTED') or
(pattern='PRIVATE') then
exit;
read_var_decs(false,false);
end;
_PROPERTY:
property_dec;
_PROCEDURE,_FUNCTION,_CLASS:
begin
oldparse_only:=parse_only;
parse_only:=true;
proc_head;
parse_only:=oldparse_only;
if (token=ID) and
((pattern='VIRTUAL') or (pattern='DYNAMIC')) then
begin
if actmembertype=sp_private then
Message(parser_w_priv_meth_not_virtual);
consume(ID);
consume(SEMICOLON);
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or povirtualmethod;
aktclass^.options:=aktclass^.options or oo_hasvirtual;
end
else if (token=ID) and (pattern='OVERRIDE') then
begin
consume(ID);
consume(SEMICOLON);
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or pooverridingmethod or povirtualmethod;
end;
{ Delphi II extension }
if (token=ID) and (pattern='ABSTRACT') then
begin
consume(ID);
consume(SEMICOLON);
if (aktprocsym^.definition^.options and povirtualmethod)<>0 then
begin
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or
poabstractmethod;
end
else
Message(parser_e_only_virtual_methods_abstract);
{ the method is defined }
aktprocsym^.definition^.forwarddef:=false;
end;
if (token=ID) and (pattern='STATIC') and
(cs_static_keyword in aktswitches) then
begin
consume(ID);
consume(SEMICOLON);
aktprocsym^.properties:=
aktprocsym^.properties or
sp_static;
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or
postaticmethod;
end;
end;
_CONSTRUCTOR:
begin
if actmembertype<>sp_public then
Message(parser_e_constructor_cannot_be_private);
oldparse_only:=parse_only;
parse_only:=true;
constructor_head;
parse_only:=oldparse_only;
if (token=ID) and
((pattern='VIRTUAL') or (pattern='DYNAMIC')) then
Message(parser_e_constructor_cannot_be_not_virtual);
end;
_DESTRUCTOR:
begin
if there_is_a_destructor then
Message(parser_n_only_one_destructor);
there_is_a_destructor:=true;
if actmembertype<>sp_public then
Message(parser_e_destructor_cannot_be_private);
oldparse_only:=parse_only;
parse_only:=true;
destructor_head;
parse_only:=oldparse_only;
if (token=ID) and
((pattern='VIRTUAL') or (pattern='DYNAMIC')) then
begin
consume(ID);
consume(SEMICOLON);
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or povirtualmethod;
end
else if (token=ID) and (pattern='OVERRIDE') then
begin
consume(ID);
consume(SEMICOLON);
aktprocsym^.definition^.options:=
aktprocsym^.definition^.options or pooverridingmethod or povirtualmethod;
end;
end;
_END : exit;
else Message(parser_e_syntax_error);
end;
until false;
end;
var
hs : string;
pcrd : pclassrefdef;
hp1 : pdef;
oldprocsym:Pprocsym;
begin
{Nowadays aktprocsym may already have a value, so we need to save
it.}
oldprocsym:=aktprocsym;
{ forward is resolved }
if assigned(fd) then
fd^.options:=fd^.options and not(oo_isforward);
there_is_a_destructor:=false;
actmembertype:=sp_public;
{ objects and class types can't be declared local }
if (symtablestack^.symtabletype<>globalsymtable) and
(symtablestack^.symtabletype<>staticsymtable) then
Message(parser_e_no_local_objects);
{ distinguish classes and objects }
if token=_OBJECT then
begin
is_a_class:=false;
consume(_OBJECT)
end
else
begin
is_a_class:=true;
consume(_CLASS);
if not(assigned(fd)) and (token=_OF) then
begin
{ a hack, but it's easy to handle }
{ class reference type }
consume(_OF);
if typecanbeforward then
forwardsallowed:=true;
hp1:=single_type(hs);
{ accept hp1, if is a forward def ...}
if ((lasttypesym<>nil)
and ((lasttypesym^.properties and sp_forwarddef)<>0)) or
{ or a class
(if the foward defined type is a class is checked, when
the forward is resolved)
}
((hp1^.deftype=objectdef) and (
(pobjectdef(hp1)^.options and oois_class)<>0)) then
begin
pcrd:=new(pclassrefdef,init(hp1));
object_dec:=pcrd;
{I add big troubles here
with var p : ^byte in graph.putimage
because a save_forward was called and
no resolve forward
=> so the definition was rewritten after
having been disposed !!
Strange problems appeared !!!!}
{Anyhow forwards should only be allowed
inside a type statement ??
don't you think so }
if (lasttypesym<>nil)
and ((lasttypesym^.properties and sp_forwarddef)<>0) then
lasttypesym^.forwardpointer:=ppointerdef(pcrd);
forwardsallowed:=false;
end
else
begin
Message(parser_e_class_type_expected);
object_dec:=new(perrordef,init);
end;
exit;
end
{ forward class }
else if not(assigned(fd)) and (token=SEMICOLON) then
begin
{ also anonym objects aren't allow (o : object a : longint; end;) }
if n='' then
Message(parser_e_no_anonym_objects);
if n='TOBJECT' then
begin
aktclass:=new(pobjectdef,init(n,nil));
class_tobject:=aktclass;
end
else
aktclass:=new(pobjectdef,init(n,class_tobject));
aktclass^.options:=aktclass^.options or oois_class or oo_isforward;
object_dec:=aktclass;
exit;
end;
end;
{ also anonym objects aren't allow (o : object a : longint; end;) }
if n='' then
Message(parser_e_no_anonym_objects);
{ read the parent class }
if token=LKLAMMER then
begin
consume(LKLAMMER);
{ does not allow objects.tobject !! }
{if token<>ID then
consume(ID);
getsym(pattern,true);}
childof:=pobjectdef(id_type(pattern));
if (childof^.deftype<>objectdef) then
begin
Message(parser_e_class_type_expected);
childof:=nil;
end;
{ a mix of class and object isn't allowed }
if (((childof^.options and oois_class)<>0) and not is_a_class) or
(((childof^.options and oois_class)=0) and is_a_class) then
Message(parser_e_mix_of_classes_and_objects);
consume(RKLAMMER);
if assigned(fd) then
begin
fd^.childof:=childof;
aktclass:=fd;
end
else
aktclass:=new(pobjectdef,init(n,childof));
end
{ if no parent class, then a class get tobject as parent }
else if is_a_class then
begin
{ is the current class tobject? }
{ so you could define your own tobject }
if n='TOBJECT' then
begin
if assigned(fd) then
aktclass:=fd
else
aktclass:=new(pobjectdef,init(n,nil));
class_tobject:=aktclass;
end
else
begin
childof:=class_tobject;
if assigned(fd) then
begin
aktclass:=fd;
aktclass^.childof:=childof;
end
else
aktclass:=new(pobjectdef,init(n,childof));
end;
end
else aktclass:=new(pobjectdef,init(n,nil));
{ set the class attribute }
if is_a_class then
aktclass^.options:=aktclass^.options or oois_class;
aktobjectdef:=aktclass;
{ default access is public }
actmembertype:=sp_public;
aktclass^.publicsyms^.next:=symtablestack;
symtablestack:=aktclass^.publicsyms;
procinfo._class:=aktclass;
testcurobject:=1;
curobjectname:=n;
while token<>_END do
begin
if (token=ID) and (pattern='PRIVATE') then
begin
consume(ID);
actmembertype:=sp_private;
current_object_option:=sp_private;
end;
if (token=ID) and (pattern='PROTECTED') then
begin
consume(ID);
current_object_option:=sp_protected;
actmembertype:=sp_protected;
end;
if (token=ID) and (pattern='PUBLIC') then
begin
consume(ID);
current_object_option:=sp_public;
actmembertype:=sp_public;
end;
if (token=ID) and (pattern='PUBLISHED') then
begin
consume(ID);
current_object_option:=sp_public;
actmembertype:=sp_public;
end;
object_komponenten;
end;
current_object_option:=sp_public;
consume(_END);
testcurobject:=0;
curobjectname:='';
{$ifdef MAKELIB}
datasegment^.concat(new(pai_cut,init));
{$endif MAKELIB}
{$ifdef GDB}
{ generate the VMT }
if cs_debuginfo in aktswitches then
begin
do_count_dbx:=true;
if assigned(aktclass^.owner) and assigned(aktclass^.owner^.name) then
debuglist^.concat(new(pai_stabs,init(strpnew('"vmt_'+aktclass^.owner^.name^+n+':S'+
typeglobalnumber('__vtbl_ptr_type')+'",'+tostr(N_STSYM)+',0,0,'+aktclass^.vmt_mangledname))));
end;
{$endif * GDB *}
datasegment^.concat(new(pai_symbol,init_global(aktclass^.vmt_mangledname)));
{ determine the size with publicsyms^.datasize, because }
{ size gives back 4 for CLASSes }
datasegment^.concat(new(pai_const,init_32bit(aktclass^.publicsyms^.datasize)));
datasegment^.concat(new(pai_const,init_32bit(-aktclass^.publicsyms^.datasize)));
{ write pointer to parent VMT, this isn't implemented in TP }
{ but this is not used in FPC ? (PM) }
{ it's not used yet, but the delphi-operators as and is need it (FK) }
if assigned(aktclass^.childof) then
begin
datasegment^.concat(new(pai_const,init_symbol(strpnew(aktclass^.childof^.vmt_mangledname))));
if aktclass^.childof^.owner^.symtabletype=unitsymtable then
concat_external(aktclass^.childof^.vmt_mangledname,EXT_NEAR);
end
else
datasegment^.concat(new(pai_const,init_32bit(0)));
{ this generates the entries }
genvmt(aktclass);
{ restore old state }
symtablestack:=symtablestack^.next;
procinfo._class:=nil;
{Restore the aktprocsym.}
aktprocsym:=oldprocsym;
object_dec:=aktclass;
end;
{ reads a record declaration }
function record_dec : pdef;
var
symtable : psymtable;
begin
symtable:=new(psymtable,init(recordsymtable));
symtable^.next:=symtablestack;
symtablestack:=symtable;
consume(_RECORD);
read_var_decs(true,false);
{ may be scale record size to a size of n*4 ? }
if ((symtablestack^.datasize mod aktpackrecords)<>0) then
inc(symtablestack^.datasize,aktpackrecords-(symtablestack^.datasize mod aktpackrecords));
consume(_END);
symtablestack:=symtable^.next;
record_dec:=new(precdef,init(symtable));
end;
{ reads a type definition and returns a pointer to it }
function read_type(const name : stringid) : pdef;
function handle_procvar:Pprocvardef;
var
sc : pstringcontainer;
s : string;
p : pdef;
varspez : tvarspez;
procvardef : pprocvardef;
begin
procvardef:=new(pprocvardef,init);
if token=LKLAMMER then
begin
consume(LKLAMMER);
inc(testcurobject);
repeat
if token=_VAR then
begin
consume(_VAR);
varspez:=vs_var;
end
else if token=_CONST then
begin
consume(_CONST);
varspez:=vs_const;
end
else varspez:=vs_value;
sc:=idlist;
if token=COLON then
begin
consume(COLON);
if token=_ARRAY then
begin
if (varspez<>vs_const) and
(varspez<>vs_var) then
begin
varspez:=vs_const;
Message(parser_e_illegal_open_parameter);
end;
consume(_ARRAY);
consume(_OF);
{ define range and type of range }
p:=new(parraydef,init(0,-1,s32bitdef));
{ define field type }
parraydef(p)^.definition:=single_type(s);
end
else
p:=single_type(s);
end
else
p:=new(pformaldef,init);
s:=sc^.get;
while s<>'' do
begin
procvardef^.concatdef(p,varspez);
s:=sc^.get;
end;
dispose(sc,done);
if token=SEMICOLON then consume(SEMICOLON)
else break;
until false;
dec(testcurobject);
consume(RKLAMMER);
end;
handle_procvar:=procvardef;
end;
var
hp1,p : pdef;
pt : ptree;
aufdef : penumdef;
aufsym : penumsym;
ap : parraydef;
s : stringid;
l,v,oldaktpackrecords : longint;
hs : string;
procedure range_type;
begin
{ it can be only a range type }
pt:=expr;
do_firstpass(pt);
{ valid expression ? }
if (pt^.treetype<>rangen) or
(pt^.left^.treetype<>ordconstn) then
Begin
Message(sym_e_error_in_type_def);
{ Here we create a node type with a range of 0 }
{ To make sure that no crashes will occur later }
{ on in the compiler. }
p:=new(porddef,init(uauto,0,0));
end
else
p:=new(porddef,init(uauto,pt^.left^.value,pt^.right^.value));
disposetree(pt);
end;
begin
case token of
ID,_STRING,_FILE:
p:=single_type(hs);
LKLAMMER:
begin
consume(LKLAMMER);
l:=-1;
aufsym := Nil;
aufdef:=new(penumdef,init);
repeat
s:=pattern;
consume(ID);
if token=ASSIGNMENT then
begin
consume(ASSIGNMENT);
v:=get_intconst;
{ please leave that a note, allows type save }
{ declarations in the win32 units ! }
if v<=l then
Message(parser_n_duplicate_enum);
l:=v;
end
else
inc(l);
constsymtable^.insert(new(penumsym,init(s,aufdef,l)));
if token=COMMA then
consume(COMMA)
else
break;
until false;
aufdef^.max:=l;
p:=aufdef;
consume(RKLAMMER);
end;
_ARRAY:
begin
consume(_ARRAY);
consume(LECKKLAMMER);
p:=nil;
repeat
{ read the expression and check it }
pt:=expr;
if pt^.treetype=typen then
begin
if pt^.resulttype^.deftype=enumdef then
begin
if p=nil then
begin
ap:=new(parraydef,
init(0,penumdef(pt^.resulttype)^.max,pt^.resulttype));
p:=ap;
end
else
begin
ap^.definition:=new(parraydef,
init(0,penumdef(pt^.resulttype)^.max,pt^.resulttype));
ap:=parraydef(ap^.definition);
end;
end
else if pt^.resulttype^.deftype=orddef then
begin
case porddef(pt^.resulttype)^.typ of
s8bit,u8bit,s16bit,u16bit,s32bit :
begin
if p=nil then
begin
ap:=new(parraydef,init(porddef(pt^.resulttype)^.von,
porddef(pt^.resulttype)^.bis,pt^.resulttype));
p:=ap;
end
else
begin
ap^.definition:=new(parraydef,init(porddef(pt^.resulttype)^.von,
porddef(pt^.resulttype)^.bis,pt^.resulttype));
ap:=parraydef(ap^.definition);
end;
end;
bool8bit:
begin
if p=nil then
begin
ap:=new(parraydef,init(0,1,pt^.resulttype));
p:=ap;
end
else
begin
ap^.definition:=new(parraydef,init(0,1,pt^.resulttype));
ap:=parraydef(ap^.definition);
end;
end;
uchar:
begin
if p=nil then
begin
ap:=new(parraydef,init(0,255,pt^.resulttype));
p:=ap;
end
else
begin
ap^.definition:=new(parraydef,init(0,255,pt^.resulttype));
ap:=parraydef(ap^.definition);
end;
end;
else Message(sym_e_error_in_type_def);
end;
end
else Message(sym_e_error_in_type_def);
end
else
begin
do_firstpass(pt);
if (pt^.treetype<>rangen) or
(pt^.left^.treetype<>ordconstn) then
Message(sym_e_error_in_type_def);
{ Registrierung der Grenzen erzwingen: }
{$IfNdef GDB}
if pt^.right^.resulttype=pdef(s32bitdef) then
pt^.right^.resulttype:=new(porddef,init(
s32bit,$80000000,$7fffffff));
{$EndIf GDB}
if p=nil then
begin
ap:=new(parraydef,init(pt^.left^.value,pt^.right^.value,pt^.right^.resulttype));
p:=ap;
end
else
begin
ap^.definition:=new(parraydef,init(pt^.left^.value,pt^.right^.value,pt^.right^.resulttype));
ap:=parraydef(ap^.definition);
end;
end;
disposetree(pt);
if token=COMMA then consume(COMMA)
else break;
until false;
consume(RECKKLAMMER);
consume(_OF);
hp1:=read_type('');
{ if no error, set element type }
if assigned(ap) then
ap^.definition:=hp1;
end;
_SET:
begin
consume(_SET);
consume(_OF);
hp1:=read_type('');
case hp1^.deftype of
enumdef : p:=new(psetdef,init(hp1,penumdef(hp1)^.max));
orddef : begin
case porddef(hp1)^.typ of
uchar : p:=new(psetdef,init(hp1,255));
u8bit,s8bit,u16bit,s16bit,s32bit :
begin
if (porddef(hp1)^.von>=0) then
p:=new(psetdef,init(hp1,porddef(hp1)^.bis))
else Message(sym_e_ill_type_decl_set);
end;
else Message(sym_e_ill_type_decl_set);
end;
end;
else Message(sym_e_ill_type_decl_set);
end;
end;
CARET:
begin
consume(CARET);
{ forwards allowed only inside TYPE statements }
if typecanbeforward then
forwardsallowed:=true;
hp1:=single_type(hs);
p:=new(ppointerdef,init(hp1));
{$ifndef GDB}
if lasttypesym<>nil then
save_forward(ppointerdef(p),lasttypesym);
{$else * GDB *}
{I add big troubles here
with var p : ^byte in graph.putimage
because a save_forward was called and
no resolve forward
=> so the definition was rewritten after
having been disposed !!
Strange problems appeared !!!!}
{Anyhow forwards should only be allowed
inside a type statement ??
don't you think so }
if (lasttypesym<>nil)
and ((lasttypesym^.properties and sp_forwarddef)<>0) then
lasttypesym^.forwardpointer:=ppointerdef(p);
{$endif * GDB *}
forwardsallowed:=false;
end;
_RECORD:
p:=record_dec;
_PACKED:
begin
consume(_PACKED);
oldaktpackrecords:=aktpackrecords;
aktpackrecords:=1;
p:=record_dec;
aktpackrecords:=oldaktpackrecords;
end;
_CLASS,
_OBJECT:
p:=object_dec(name,nil);
_PROCEDURE:
begin
consume(_PROCEDURE);
p:=handle_procvar;
end;
_FUNCTION:
begin
consume(_FUNCTION);
p:=handle_procvar;
consume(COLON);
pprocvardef(p)^.retdef:=single_type(hs);
end;
else
range_type;
end;
read_type:=p;
end;
{ search in symtablestack used, but not defined type }
procedure testforward_types(p : psym);{$ifndef FPC}far;{$endif}
begin
if (p^.typ=typesym) and ((p^.properties and sp_forwarddef)<>0) then
Message(sym_e_type_id_not_defined);
end;
{ reads a type declaration to the symbol table }
procedure type_dec;
var
typename : stringid;
{$ifdef dummy}
olddef,newdef : pdef;
s : string;
{$endif dummy}
begin
parse_types:=true;
consume(_TYPE);
typecanbeforward:=true;
repeat
typename:=pattern;
consume(ID);
consume(EQUAL);
{ here you loose the strictness of pascal
for which a redefinition like
childtype = parenttype;
child2type = parenttype;
does not make the two child types equal !!
here all vars from childtype and child2type
get the definition of parenttype !! }
{$ifdef testequaltype}
if (token = ID) or (token=_FILE) or (token=_STRING) then
begin
olddef := single_type(s);
{ make a clone of olddef }
{ is that ok ??? }
getmem(newdef,SizeOf(olddef));
move(olddef^,newdef^,SizeOf(olddef));
symtablestack^.insert(new(ptypesym,init(typename,newdef)));
end
else
{$endif testequaltype}
begin
getsym(typename,false);
{ check if it is the definition of a forward defined class }
if assigned(srsym) and (token=_CLASS) and
(srsym^.typ=typesym) and
(ptypesym(srsym)^.definition^.deftype=objectdef) and
((pobjectdef(ptypesym(srsym)^.definition)^.options and oo_isforward)<>0) and
((pobjectdef(ptypesym(srsym)^.definition)^.options and oois_class)<>0) then
begin
{ we can ignore the result }
{ the definition is modified }
object_dec(typename,pobjectdef(ptypesym(srsym)^.definition));
end
else
symtablestack^.insert(new(ptypesym,init(typename,read_type(typename))));
end;
consume(SEMICOLON);
until token<>ID;
typecanbeforward:=false;
{$ifdef tp}
symtablestack^.foreach(testforward_types);
{$else}
symtablestack^.foreach(@testforward_types);
{$endif}
resolve_forwards;
parse_types:=false;
end;
{ parses varaible declarations and inserts them in }
{ the top symbol table of symtablestack }
procedure var_dec;
{var
p : pdef;
sc : pstringcontainer; }
begin
consume(_VAR);
read_var_decs(false,true);
end;
{ reads the filed of a record into a }
{ symtablestack, if record=false }
{ variants are forbidden, so this procedure }
{ can be used to read object fields }
{ if absolute is true, ABSOLUTE and file }
{ types are allowed }
{ => the procedure is also used to read }
{ a sequence of variable declaration }
procedure read_var_decs(is_record : boolean;do_absolute : boolean);
var
sc : pstringcontainer;
s : stringid;
l : longint;
code : word;
hs : string;
p,casedef : pdef;
{ maxsize contains the max. size of a variant }
{ startvarrec contains the start of the variant part of a record }
maxsize,startvarrec : longint;
pt : ptree;
old_parse_types : boolean;
{ to handle absolute }
abssym : pabsolutesym;
begin
hs:='';
old_parse_types:=parse_types;
parse_types:=true;
while (token=ID) and
(pattern<>'PUBLIC') and
(pattern<>'PRIVATE') and
(pattern<>'PUBLISHED') and
(pattern<>'PROTECTED') do
begin
sc:=idlist;
consume(COLON);
p:=read_type('');
if do_absolute and (token=ID) and (pattern='ABSOLUTE') then
begin
s:=sc^.get;
if sc^.get<>'' then
Message(parser_e_absolute_only_one_var);
dispose(sc,done);
consume(ID);
if token=ID then
begin
getsym(pattern,true);
consume(ID);
{ we should check the result type of srsym }
if not (srsym^.typ in [varsym,typedconstsym]) then
Message(parser_e_absolute_only_to_var_or_const);
abssym:=new(pabsolutesym,init(s,p));
abssym^.typ:=absolutesym;
abssym^.abstyp:=tovar;
abssym^.ref:=srsym;
symtablestack^.insert(abssym);
end
else
if token=CSTRING then
begin
abssym:=new(pabsolutesym,init(s,p));
s:=pattern;
consume(CSTRING);
abssym^.typ:=absolutesym;
abssym^.abstyp:=toasm;
abssym^.asmname:=stringdup(s);
symtablestack^.insert(abssym);
end
else
{ absolute address ?!? }
if token=INTCONST then
begin
if (target_info.target=target_GO32V2) then
begin
abssym:=new(pabsolutesym,init(s,p));
abssym^.typ:=absolutesym;
abssym^.abstyp:=toaddr;
abssym^.absseg:=false;
s:=pattern;
consume(INTCONST);
val(s,abssym^.address,code);
if token=COLON then
begin
consume(token);
s:=pattern;
consume(INTCONST);
val(s,l,code);
abssym^.address:=abssym^.address shl 4+l;
abssym^.absseg:=true;
end;
symtablestack^.insert(abssym);
end
else
Message(parser_e_absolute_only_to_var_or_const);
end
else
Message(parser_e_absolute_only_to_var_or_const);
end
else
begin
if token=SEMICOLON then
begin
if (symtablestack^.symtabletype=objectsymtable) then
begin
consume(SEMICOLON);
if (token=ID) and (pattern='STATIC') and
(cs_static_keyword in aktswitches) then
begin
current_object_option:=current_object_option or sp_static;
insert_syms(symtablestack,sc,p);
current_object_option:=current_object_option - sp_static;
consume(ID);
consume(SEMICOLON);
end
else
{ this will still be a the wrong line !! }
insert_syms(symtablestack,sc,p);
end
else
begin
{ at the right line }
insert_syms(symtablestack,sc,p);
consume(SEMICOLON);
end
end
else
begin
insert_syms(symtablestack,sc,p);
if not(is_record) then
consume(SEMICOLON);
end;
end;
while token=SEMICOLON do
consume(SEMICOLON);
end;
if (token=_CASE) and is_record then
begin
maxsize:=0;
consume(_CASE);
s:=pattern;
getsym(s,false);
{ may be only a type: }
if assigned(srsym) and ((srsym^.typ=typesym) or
{ and with unit qualifier: }
(srsym^.typ=unitsym)) then
begin
casedef:=read_type('');
end
else
begin
consume(ID);
consume(COLON);
casedef:=read_type('');
symtablestack^.insert(new(pvarsym,init(s,casedef)));
end;
if not is_ordinal(casedef) then
Message(parser_e_ordinal_expected);
consume(_OF);
startvarrec:=symtablestack^.datasize;
repeat
repeat
pt:=expr;
do_firstpass(pt);
if not(pt^.treetype=ordconstn) then
Message(cg_e_illegal_expression);
disposetree(pt);
if token=COMMA then consume(COMMA)
else break;
until false;
consume(COLON);
consume(LKLAMMER);
if token<>RKLAMMER then
read_var_decs(true,false);
{ calculates maximal variant size }
maxsize:=max(maxsize,symtablestack^.datasize);
{ the items of the next variant are overlayed }
symtablestack^.datasize:=startvarrec;
consume(RKLAMMER);
if token<>SEMICOLON then
break
else
consume(SEMICOLON);
while token=SEMICOLON do
consume(SEMICOLON);
until (token=_END) or (token=RKLAMMER);
{ at last set the record size to that of the biggest variant }
symtablestack^.datasize:=maxsize;
end;
parse_types:=old_parse_types;
end;
procedure read_declarations(islibrary : boolean);
begin
repeat
case token of
_LABEL : label_dec;
_CONST : const_dec;
_TYPE : type_dec;
_VAR : var_dec;
_CONSTRUCTOR,_DESTRUCTOR,
_FUNCTION,_PROCEDURE,_OPERATOR,_CLASS : unter_dec;
_EXPORTS : if islibrary then
read_exports
else
break;
else break;
end;
until false;
end;
procedure read_interface_declarations;
begin
{Since the body is now parsed at lexlevel 1, and the declarations
must be parsed at the same lexlevel we increase the lexlevel.}
inc(lexlevel);
repeat
case token of
_CONST : const_dec;
_TYPE : type_dec;
_VAR : var_dec;
{ should we allow operator in interface ? }
{ of course otherwise you cannot }
{ declare an operator usable by other }
{ units or progs PM }
_FUNCTION,_PROCEDURE,_OPERATOR : unter_dec;
else
break;
end;
until false;
dec(lexlevel);
end;
end.
{
$Log: pdecl.pas,v $
Revision 1.1.1.1.2.2 1998/04/27 23:07:02 peter
* small message fixes
Revision 1.1.1.1.2.1 1998/04/06 16:21:10 peter
* carl and mine bugfixes from the mainbranch applied
Revision 1.1.1.1 1998/03/25 11:18:14 root
* Restored version
Revision 1.31 1998/03/24 21:48:33 florian
* just a couple of fixes applied:
- problem with fixed16 solved
- internalerror 10005 problem fixed
- patch for assembler reading
- small optimizer fix
- mem is now supported
Revision 1.30 1998/03/21 23:59:39 florian
* indexed properties fixed
* ppu i/o of properties fixed
* field can be also used for write access
* overriding of properties
Revision 1.29 1998/03/18 22:50:11 florian
+ fstp/fld optimization
* routines which contains asm aren't longer optimzed
* wrong ifdef TEST_FUNCRET corrected
* wrong data generation for array[0..n] of char = '01234'; fixed
* bug0097 is fixed partial
* bug0116 fixed (-Og doesn't use enter of the stack frame is greater than
65535)
Revision 1.28 1998/03/10 16:27:41 pierre
* better line info in stabs debug
* symtabletype and lexlevel separated into two fields of tsymtable
+ ifdef MAKELIB for direct library output, not complete
+ ifdef CHAINPROCSYMS for overloaded seach across units, not fully
working
+ ifdef TESTFUNCRET for setting func result in underfunction, not
working
Revision 1.27 1998/03/10 01:17:23 peter
* all files have the same header
* messages are fully implemented, EXTDEBUG uses Comment()
+ AG... files for the Assembler generation
Revision 1.26 1998/03/06 00:52:41 peter
* replaced all old messages from errore.msg, only ExtDebug and some
Comment() calls are left
* fixed options.pas
Revision 1.25 1998/03/05 22:43:49 florian
* some win32 support stuff added
Revision 1.24 1998/03/04 17:33:49 michael
+ Changed ifdef FPK to ifdef FPC
Revision 1.23 1998/03/04 01:35:06 peter
* messages for unit-handling and assembler/linker
* the compiler compiles without -dGDB, but doesn't work yet
+ -vh for Hint
Revision 1.22 1998/03/02 01:49:00 peter
* renamed target_DOS to target_GO32V1
+ new verbose system, merged old errors and verbose units into one new
verbose.pas, so errors.pas is obsolete
Revision 1.21 1998/02/28 14:43:47 florian
* final implemenation of win32 imports
* extended tai_align to allow 8 and 16 byte aligns
Revision 1.20 1998/02/19 00:11:07 peter
* fixed -g to work again
* fixed some typos with the scriptobject
Revision 1.19 1998/02/13 10:35:23 daniel
* Made Motorola version compilable.
* Fixed optimizer
Revision 1.18 1998/02/12 17:19:19 florian
* fixed to get remake3 work, but needs additional fixes (output, I don't like
also that aktswitches isn't a pointer)
Revision 1.17 1998/02/12 11:50:25 daniel
Yes! Finally! After three retries, my patch!
Changes:
Complete rewrite of psub.pas.
Added support for DLL's.
Compiler requires less memory.
Platform units for each platform.
Revision 1.16 1998/02/11 21:56:36 florian
* bugfixes: bug0093, bug0053, bug0088, bug0087, bug0089
Revision 1.15 1998/02/06 10:34:25 florian
* bug0082 and bug0084 fixed
Revision 1.14 1998/02/02 11:56:49 pierre
* better line info for var statement
Revision 1.13 1998/01/30 21:25:31 carl
* bugfix #86 + checking of all other macros for crashes, fixed typeof
partly among others.
Revision 1.12 1998/01/23 17:12:19 pierre
* added some improvements for as and ld :
- doserror and dosexitcode treated separately
- PATH searched if doserror=2
+ start of long and ansi string (far from complete)
in conditionnal UseLongString and UseAnsiString
* options.pas cleaned (some variables shifted to globals)gl
Revision 1.11 1998/01/21 21:25:46 florian
* small problem with variante records fixed:
case a : (x,y,z) of
...
is now allowed
Revision 1.10 1998/01/13 23:11:13 florian
+ class methods
Revision 1.9 1998/01/12 13:03:31 florian
+ parsing of class methods implemented
Revision 1.8 1998/01/11 10:54:23 florian
+ generic library support
Revision 1.7 1998/01/09 23:08:32 florian
+ C++/Delphi styled //-comments
* some bugs in Delphi object model fixed
+ override directive
Revision 1.6 1998/01/09 18:01:16 florian
* VIRTUAL isn't anymore a common keyword
+ DYNAMIC is equal to VIRTUAL
Revision 1.5 1998/01/09 16:08:23 florian
* abstract methods call now abstracterrorproc if they are called
a class with an abstract method can be create with a class reference else
the compiler forbides this
Revision 1.4 1998/01/09 13:39:55 florian
* public, protected and private aren't anymore key words
+ published is equal to public
Revision 1.3 1998/01/09 13:18:12 florian
+ "forward" class declarations (type tclass = class; )
Revision 1.2 1998/01/09 09:09:58 michael
+ Initial implementation, second try
}